package com.example.hello.views.users;

import com.example.hello.*;
import com.example.hello.POJO.*;
import com.example.hello.views.Collection;
import com.example.hello.views.assignments.Assignments;
import com.example.hello.views.organizations.Organizations;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Link;
import com.vaadin.ui.VerticalLayout;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class User extends Collection {

    public static UserPOJO currentUser;

    protected BootstrapTabPanel panel = new BootstrapTabPanel(BootstrapTabPanel.Position.ABOVE);
    protected BootstrapTabLink badgesLink = new BootstrapTabLink("Badges");
    protected BootstrapTabPane badgesPane = new BootstrapTabPane();
    protected BootstrapTabLink orgsLink = new BootstrapTabLink("Basic User – Organizations");
    protected BootstrapTabPane orgsPane = new BootstrapTabPane();
    protected BootstrapTabLink assignmentsLink = new BootstrapTabLink("Reviewer – Assignments");
    protected BootstrapTabPane assignmentsPane = new BootstrapTabPane();
    protected BootstrapTabLink assignedOrgsLink = new BootstrapTabLink("Assignment Admin – Organizations");
    protected BootstrapTabPane assignedOrgsPane = new BootstrapTabPane();
    protected BootstrapTabLink submissionsLink = new BootstrapTabLink("Submissions");
    protected BootstrapTabPane submissionsPane = new BootstrapTabPane();

    protected Link editUser;

    protected UserPOJO user;
    protected List<BadgePOJO> badges;

    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        currentUser = ((HelloUI) HelloUI.getCurrent()).getUser();
        if (currentUser == null) currentUser = new UserPOJO();

        checkVisible();

        badgesLink.setActive(true);
        badgesPane.setActive(true);
    }

    protected void checkVisible() {
        if (currentUser.isSiteAdmin() || currentUser.equals(user)) {
            editUser.setVisible(true);
        } else {
            editUser.setVisible(false);
        }

        if (user.getOrganizations().isEmpty() && user.getAssignments().isEmpty() && user.getAssignedOrgs().isEmpty()) {
            orgsLink.setVisible(true);
            orgsPane.setVisible(true);
        } else {
            orgsLink.setVisible(!user.getOrganizations().isEmpty());
            orgsPane.setVisible(!user.getOrganizations().isEmpty());
        }
        assignmentsLink.setVisible(!user.getAssignments().isEmpty());
        assignmentsPane.setVisible(!user.getAssignments().isEmpty());
        assignedOrgsLink.setVisible(!user.getAssignedOrgs().isEmpty());
        assignedOrgsPane.setVisible(!user.getAssignedOrgs().isEmpty());
        submissionsLink.setVisible(true);
        submissionsPane.setVisible(true);
    }

    public User(String NAME, boolean tab) {
        this(NAME, tab, null, null);
    }

    public User(String NAME, boolean tab, UserPOJO user, List<BadgePOJO> badges) {
        super(NAME);
        this.user = user;
        this.badges = badges;

        if (this.user == null) getUser();
        if (this.badges == null) getBadges();
        if (!tab) createTabViews();

        initTabPanel();
        initUser();
    }

    private void getUser() {
        user = GET(NAME);
    }

    private void getBadges() {
        badges = new ArrayList<>();
        for (Object b : user.getBadges().toArray()) {
            badges.add(com.example.hello.views.badges.Badge.GETBadge((String) b));
        }
    }

    private void createTabViews() {
        User_Badges badgesTab = new User_Badges(NAME, user, badges);
        HelloUI.getCurrent().getNavigator().addView(badgesTab.NAME, badgesTab);

        User_Organizations orgsTab = new User_Organizations(NAME, user, badges);
        HelloUI.getCurrent().getNavigator().addView(orgsTab.NAME, orgsTab);
        
        User_Assignments assignmentsTab = new User_Assignments(NAME, user, badges);
        HelloUI.getCurrent().getNavigator().addView(assignmentsTab.NAME, assignmentsTab);

        User_AssignedOrgs assignedOrgsTab = new User_AssignedOrgs(NAME, user, badges);
        HelloUI.getCurrent().getNavigator().addView(assignedOrgsTab.NAME, assignedOrgsTab);

        User_Submissions submissionsTab = new User_Submissions(NAME, user, badges);
        HelloUI.getCurrent().getNavigator().addView(submissionsTab.NAME, submissionsTab);
    }

    public void initTabPanel() {
        panel.setSizeFull();

        badgesLink.setTabPane(badgesPane);
        badgesLink.setDataTarget("badges");
        badgesPane.setHref("badges");
        orgsLink.setTabPane(orgsPane);
        orgsLink.setDataTarget("orgs");
        orgsPane.setHref("orgs");
        assignmentsLink.setTabPane(assignmentsPane);
        assignmentsLink.setDataTarget("assignments");
        assignmentsPane.setHref("assignments");
        assignedOrgsLink.setTabPane(assignedOrgsPane);
        assignedOrgsLink.setDataTarget("assignedOrgs");
        assignedOrgsPane.setHref("assignedOrgs");
        submissionsLink.setTabPane(submissionsPane);
        submissionsLink.setDataTarget("tab");
        submissionsPane.setHref("tab");

        panel.addTab(badgesLink);
        panel.addTab(badgesPane);
        panel.addTab(orgsLink);
        panel.addTab(orgsPane);
        panel.addTab(assignmentsLink);
        panel.addTab(assignmentsPane);
        panel.addTab(assignedOrgsLink);
        panel.addTab(assignedOrgsPane);
        panel.addTab(submissionsLink);
        panel.addTab(submissionsPane);

        String base = "users/" + user.getUserName();
        final String badgesUri       = base + "?tab=badges";
        final String orgsUri         = base + "?tab=organizations";
        final String assignmentsUri  = base + "?tab=assignments";
        final String assignedOrgsUri = base + "?tab=assignedOrgs";
        final String submissionsUri  = base + "?tab=submissions";
        badgesLink.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(badgesUri);
            }
        });
        orgsLink.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(orgsUri);
            }
        });
        assignmentsLink.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(assignmentsUri);
            }
        });
        assignedOrgsLink.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(assignedOrgsUri);
            }
        });
        submissionsLink.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(submissionsUri);
            }
        });
    }

    public void initUser() {
        setWidth("1000px");

        HorizontalLayout top = new HorizontalLayout();
        HorizontalLayout mid = new HorizontalLayout();
        HorizontalLayout bot = new HorizontalLayout();

        initTop(top);
        initMid(mid);
        initBot(bot);

        // add to parent
        addComponents(
                top,
                new VSpacer(20),
                mid,
                new VSpacer(20),
                bot
        );
        setExpandRatio(mid, 1);
        setExpandRatio(bot, 1);
    }

    private void initTop(HorizontalLayout top) {
        top.setWidth("100%");
        top.addStyleName("border-bottom");

        /** top_left **/
        VerticalLayout top_left = new VerticalLayout();
        top_left.setHeight("80px");

        Heading userName = new Heading(user.getUserName(), 1);
        Heading role = new Heading("&#8196" + (user.getRoles()), 6);

        top_left.addComponents(userName, role);
        top_left.setComponentAlignment(role, Alignment.BOTTOM_LEFT);
        top_left.setExpandRatio(userName, 3);
        top_left.setExpandRatio(role, 2);

        /** top_right **/
        HorizontalLayout top_right = new HorizontalLayout();
        top_right.setSizeFull();

        editUser = new Link("Edit Profile", new ExternalResource("http://localhost:8079/#!edit_user/" + user.getEncodedName()));

        top_right.addComponent(editUser);
        top_right.setComponentAlignment(editUser, Alignment.BOTTOM_RIGHT);

        // add to top
        top.addComponents(top_left, top_right);
        top.setComponentAlignment(top_right, Alignment.BOTTOM_RIGHT);
    }

    private void initMid(HorizontalLayout mid) {
        mid.setWidth("400px");
        mid.setHeight("100px");

        Heading fullName = new Heading(user.getFullName(false), 4);
        Heading reputation = new Heading(Integer.toString(user.getReputation()), 4);
        Heading email = new Heading(user.getEmail(), 4);

        // add to mid
        mid.addComponents(fullName, reputation, email);
    }

    private void initBot(HorizontalLayout bot) {
        bot.setWidth("100%");
        bot.setHeight("300px");

        /** bot_left **/
        VerticalLayout bot_left = new VerticalLayout();
        bot_left.setWidth("713px");

        // add badges
        VerticalLayout badgesLayout = new VerticalLayout();

        for (BadgePOJO badge : badges) {
            HorizontalLayout badgeLayout = new HorizontalLayout();
            BootstrapParagraph badgeName = new BootstrapParagraph(badge.getDecodedName(), "MIDDLE_CENTER", 0);

            badgeLayout.addComponents(
                    badge.createBadge(),
                    new HSpacer(5),
                    badgeName
            );
            badgeLayout.setComponentAlignment(badgeName, Alignment.MIDDLE_CENTER);

            badgesLayout.addComponents(badgeLayout, new VSpacer(7));
        }
        badgesPane.addWidget(badgesLayout);

        // add organizations
        for (Object org : user.getOrganizations().toArray()) {
            AnchorHref href = new AnchorHref("http://localhost:8079/#!organizations/" + org, (String) org);
            orgsPane.addWidget(new Heading(href.toString(), 5));
        }

        // add assignments
        for (Object assignment : user.getAssignments().toArray()) {
            AnchorHref href = new AnchorHref("http://localhost:8079/#!assignments/" + assignment,
                    (String) assignment);
            assignmentsPane.addWidget(new Heading(href.toString(), 5));
        }

        // add assignedOrgs
        for (Object assignedOrg : user.getAssignedOrgs().toArray()) {
            AnchorHref href = new AnchorHref("http://localhost:8079/#!organizations/" + assignedOrg,
                    (String) assignedOrg);
            assignedOrgsPane.addWidget(new Heading(href.toString(), 5));
        }

        // add submissions
        File submissions = new File("/users/daneorie/Desktop/test/.users/" + user.get_id());
        if (!submissions.exists() && !submissions.mkdirs())
            throw new IllegalStateException("Couldn't create dir: " + submissions);
        VerticalLayout submissionsLayout = new VerticalLayout();
        for (File file : submissions.listFiles()) {
            submissionsLayout.addComponent(new Link(file.getName(), new FileResource(file)));
        }
        submissionsPane.addWidget(submissionsLayout);

        // add panel
        bot_left.addComponent(panel);

        /** bot_right **/
        VerticalLayout bot_right = new VerticalLayout();
        bot_right.setSizeFull();
        bot_right.addStyleName("border-left");
        Heading recentActivity = new Heading("Recent Activity", 3);
        recentActivity.setWidth("100%");
        recentActivity.addStyleName("border-bottom-thin");

        HorizontalLayout awardedBadge = new HorizontalLayout();
        HorizontalLayout assignmentSubmission = new HorizontalLayout();
        HorizontalLayout organizationJoined = new HorizontalLayout();
        Heading profileCreated = new Heading("Created Profile", 5);
        bot_right.addComponents(
                recentActivity,
                awardedBadge,
                new VSpacer(8),
                assignmentSubmission,
                new VSpacer(8),
                organizationJoined,
                new VSpacer(8),
                profileCreated
        );

        Heading badgeAward = new Heading("Awarded Badge 1", 5);
        Badge badge_h = new Badge("H", "228B22");
        awardedBadge.addComponents(badgeAward, new HSpacer(8), badge_h);
        awardedBadge.setComponentAlignment(badgeAward, Alignment.MIDDLE_CENTER);
        awardedBadge.setComponentAlignment(badge_h, Alignment.MIDDLE_CENTER);

        Heading assignment1 = new Heading("Submitted " + new AnchorHref("Assignment 1"), 5);
        assignmentSubmission.addComponent(assignment1);
        assignmentSubmission.setComponentAlignment(assignment1, Alignment.MIDDLE_CENTER);

        Heading org1 = new Heading("Joined " + new AnchorHref("Organization 1"), 5);
        organizationJoined.addComponent(org1);
        organizationJoined.setComponentAlignment(org1, Alignment.MIDDLE_CENTER);

        // add to bot
        bot.addComponents(bot_left, new HSpacer(15), bot_right);
//        bot.setExpandRatio(bot_left, 3);
        bot.setExpandRatio(bot_right, 1);
    }

    /**
     * These static methods are intended to be used as an alternative to the HttpRequest class
     * directly. The GET(String get) method can technically return any value type, however this
     * is useful if the user of this methods knows the type of the value that should be returned.
     *
     * These methods currently reside in this View due to this View's name relationship with
     * these methods function. It might be a good idea to move these methods elsewhere later
     * (perhaps in a helper class whose sole purpose is to return the proper POJO).
     */
    public static UserPOJO GET(String get) {
        UserPOJO user = new UserPOJO();
        try {
            String json = HttpRequest.GET(get);
            ObjectMapper mapper = new ObjectMapper();
            user = mapper.readValue(json, new TypeReference<UserPOJO>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return user;
    }

    public static UserPOJO GETUser(String userName) {
        UserPOJO user = new UserPOJO();
        try {
            String json = HttpRequest.GET(Users.NAME + "/" + userName);
            ObjectMapper mapper = new ObjectMapper();
            user = mapper.readValue(json, new TypeReference<UserPOJO>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return user;
    }

    /**
     * This method is intended to be called after changes have been made to the resource
     * associated with this View. Since the View is persistent due to its initialization
     * (unlike Users, Organizations, etc.), a new View must be created to reflect any changes.
     */
    private static void refreshView(UserPOJO user) {
        HelloUI.getCurrent().getNavigator().removeView(Users.NAME + "/" + user.getEncodedName());
        User newView = new User(user.getEncodedName(), false, user, null);
        HelloUI.getCurrent().getNavigator().addView(newView.NAME, newView);
        HelloUI.getCurrent().getNavigator().navigateTo(newView.NAME);
    }

    /**
     * The methods below are intended to provide the proper JOIN mechanism between
     * related resources.
     */
    public static boolean changeUserName(UserPOJO user, String oldUserName, String newUserName) {
        Set<OrganizationPOJO> orgs = Organizations.GETOrganizations("?user=" + user.getEncodedName());
        List<AssignmentPOJO> assignments = Assignments.GETAssignments("?reviewer=" + user.getEncodedName());
        Set<OrganizationPOJO> assignedOrgs = Organizations.GETOrganizations("?admin=" + user.getEncodedName());

        user.setUserName(newUserName);
        for (OrganizationPOJO org : orgs) {
            org.removeUser(oldUserName);
            org.addUser(newUserName);
            org.update();
        }
        for (AssignmentPOJO assignment : assignments) {
            assignment.removeReviewer(oldUserName);
            assignment.addReviewer(newUserName);
            assignment.update();
        }
        for (OrganizationPOJO assignedOrg : assignedOrgs) {
            assignedOrg.removeAdmin(oldUserName);
            assignedOrg.addAdmin(newUserName);
            assignedOrg.update();
        }

        return POJO.update(Users.NAME + "/" + oldUserName, user);
    }

    public static boolean addBadge(UserPOJO user, BadgePOJO badge, boolean create) {
        user.addBadge(badge.getEncodedName());
        return user.update() && (!create || badge.create());
    }

    public static boolean removeBadge(UserPOJO user, BadgePOJO badge) {
        user.removeBadge(badge.getEncodedName());
        return user.update();
    }

    public static boolean addOrg(UserPOJO user, OrganizationPOJO org, boolean create) {
        user.addOrg(org.getEncodedName());
        org.addUser(user.getEncodedName());
        return user.update() && (create ? org.create() : org.update());
    }

    public static boolean removeOrg(UserPOJO user, OrganizationPOJO org) {
        user.removeOrg(org.getEncodedName());
        org.removeUser(org.getEncodedName());
        return user.update() && org.update();
    }

    public static boolean addAssignedOrg(UserPOJO admin, OrganizationPOJO assignedOrg, boolean create) {
        admin.addAssignedOrg(assignedOrg.getEncodedName());
        assignedOrg.addAdmin(admin.getEncodedName());
        return admin.update() && (create ? assignedOrg.create() : assignedOrg.update());
    }

    public static boolean removeAssignedOrg(UserPOJO admin, OrganizationPOJO assignedOrg) {
        admin.removeAssignedOrg(assignedOrg.getEncodedName());
        assignedOrg.removeAdmin(admin.getEncodedName());
        return admin.update() && assignedOrg.update();
    }

}